#define _POSIX_SOURCE
#define _BSD_SOURCE

#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include "rngs.h"

#ifndef MAX_PLAYER
#define MAX_PLAYER 4
#endif

#ifndef SAMPLE
#define SAMPLE 50
#endif

int checkSteward(struct gameState *post, int choice1, int choice2, int choice3, int handPos) 
{
	struct gameState pre;
	
	// Save initial conditions
	int preHandCount = post->handCount[post->whoseTurn];
	int preDeckCount = post->deckCount[post->whoseTurn];
	int preDiscardCount = post->discardCount[post->whoseTurn];
	int prePlayedCardCount = post->playedCardCount;
	int preCoins = post->coins;

	/* Copy memory located at post to memory located at pre */
	memcpy(&pre, post, sizeof(struct gameState));

	cardEffect(steward, choice1, choice2, choice3, post, handPos, NULL);

	if (choice1 == 1) {
		// Hand: +2, Deck: -2, Discard: 0
		assert(post->handCount[post->whoseTurn] == preHandCount + 1);
		
		if (preDeckCount >= 2) {
			assert(post->deckCount[post->whoseTurn] == preDeckCount - 2);
			assert(post->discardCount[post->whoseTurn] == preDiscardCount);
		} else {
			assert(post->deckCount[post->whoseTurn] == preDiscardCount - (2 - preDeckCount));
			assert(post->discardCount[post->whoseTurn] == 0);
		}
		assert(post->coins == preCoins);

	} else if (choice1 == 2) {
		// Coins: +2
		assert(post->handCount[post->whoseTurn] == preHandCount - 1);
		assert(post->deckCount[post->whoseTurn] == preDeckCount);
		assert(post->discardCount[post->whoseTurn] == preDiscardCount);
		assert(post->coins == preCoins + 2);
	} else {
		assert(post->handCount[post->whoseTurn] == preHandCount - 3);
		assert(post->deckCount[post->whoseTurn] == preDeckCount);
		assert(post->discardCount[post->whoseTurn] == preDiscardCount);
	}

	// Played card must be in the playing field
	assert(post->playedCardCount == prePlayedCardCount + 1);
	assert(post->playedCards[post->playedCardCount - 1] == steward);
 
	/* Set Steward pre case */
	if (choice1 == 1) {
		drawCard(pre.whoseTurn, &pre);
		drawCard(pre.whoseTurn, &pre);
	} else if (choice1 == 2) {
		pre.coins += 2;
	} else {
		discardCard(choice2, pre.whoseTurn, &pre, 1);
		discardCard(choice3, pre.whoseTurn, &pre, 1);
	}
	discardCard(handPos, pre.whoseTurn, &pre, 0);

	if (!(choice1 == 1 && preDeckCount < 2)) {
		assert(memcmp(&pre, post, sizeof(struct gameState)) == 0);
	}

	return 0;
}

void sigabrt_catch(int sig)
{
	printf("SIGABRT has been caught\n");
	exit(0);
}

int main(int argc, char **argv)
{
	int i, n, choice1, choice2, choice3, handPos;
	struct gameState G;

	signal(SIGABRT, sigabrt_catch);

	printf("Testing cardEffect steward.\n");

	printf("RANDOM TESTS.\n");

	/* Set Random Number Generator */
	SelectStream(2);
	PutSeed(3);

	for (n = 1; n <= 20000; n++) {
		for (i = 0; i < sizeof(struct gameState); i++) {
			((char *) &G)[i] = floor(Random() * 256);
		}

		/* Randomly Set Game Numbers */
		G.numPlayers = floor(Random() * (MAX_PLAYER - 1)) + 2;
		G.whoseTurn = floor(Random() * G.numPlayers);
		G.deckCount[G.whoseTurn] = floor(Random() * MAX_DECK);
		G.discardCount[G.whoseTurn] = floor(Random() * MAX_DECK);
		G.handCount[G.whoseTurn] = floor(Random() * MAX_HAND) + 1;
		G.playedCardCount = floor(Random() * MAX_DECK);
		G.coins = floor(Random() * SAMPLE);

		/* Set choices */
		choice1 = floor(Random() * 3);
		choice2 = floor(Random() * G.handCount[G.whoseTurn]);
		choice3 = floor(Random() * G.handCount[G.whoseTurn]);

		/* Fill deck with cards */
		for (i = 0; i < G.deckCount[G.whoseTurn]; i++) {
			G.deck[G.whoseTurn][i] = floor(Random() * treasure_map);
		}

		/* Fill discard with cards */
		for (i = 0; i < G.discardCount[G.whoseTurn]; i++) {
			G.discard[G.whoseTurn][i] = floor(Random() * treasure_map);
		}

		/* Fill hand with cards */
		for (i = 0; i < G.handCount[G.whoseTurn]; i++) {
			G.hand[G.whoseTurn][i] = floor(Random() * treasure_map);
		}

		/* Set 1 random card in hand as steward */
		handPos = floor(Random() * G.handCount[G.whoseTurn]);
		G.hand[G.whoseTurn][handPos] = steward;

		if (argc > 1 && strcmp(argv[1], "-v") == 0) {
			printf("Test #%5d: plyr = %d dckCnt = %3d hndPos = %3d c1 = %d c2 = %3d c3 = %3d\n", 
					n, G.whoseTurn, G.deckCount[G.whoseTurn], handPos, choice1, choice2, choice3);
		}
		
		checkSteward(&G, choice1, choice2, choice3, handPos);
	}

	printf ("ALL TESTS OK\n");

	return 0;
}
